package thred;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 演示一个多线程的案例：
 *      浏览器访问统计，假设每一个用户访问浏览器是，浏览器后台的计数器自增，假设当前有100个用户
 *      每个用户访问10次，求出最终以用的访问次数，显然是1000，但是下面的代码演示最终结果可能并
 *      不是1000次，重点分析错误原因和解决方式
 */

/**
 * 执行五次程序的结果如下：
 *      main count: 92 totalTime: 19
 *      main count: 105 totalTime: 16
 *      main count: 110 totalTime: 14
 *      main count: 95 totalTime: 15
 *      main count: 116 totalTime: 23
 *      这个结果和我们想达到的结果相差甚远? 问题究竟出现在那里?
 */

/**
 * 问题分析：
 *      对于上述分析我们应该从修改count的方法看起，因为只有这个方法是直接对count变量进行操作的
 *      下面的代码演示中只有request()方法直接对count进行修改，我们从request()方法进行分析：
 *          public static void request() throws InterruptedException {
 *              TimeUnit.MILLISECONDS.sleep(5);
 *              count++;
 *          }
 *      这个代码乍一看没有任何逻辑问题和编码问题，但是其问题就出现在count++上，虽然只有一行代码，但是jvm底层
 *      执行了三行代码，分别是：
 *          1. 将count的值传递给A
 *          2. A变量+1，将这个结果给B
 *          3. 将B这个结果还给count
 *      由于下面的运行环境是一个多线程的运行环境，可能会出现多个线程同时对一个数据即count进行修改，假设当前有
 *      两个线程thread1，thread2通过对count 进行操作，由上述count++代码的分析，他们同获取的count是一样的
 *      最终B的结果就是count+1，所以最终两个线程同时执行完成之后count只增加了一次，这就是问题所在
 */

/**
 * 问题的解决方案：
 *      1. 将request方法设置为同步方法 添加synchronized关键字在request的方法上
 */
@SuppressWarnings("all")
public class demo {
    // 访问计数器
    static int count = 0;

    // 模拟用户的请求方法
    public static void request() throws InterruptedException {
        // 延迟5ms，模拟用户访问网站
        TimeUnit.MILLISECONDS.sleep(5);
        // 模拟后台访问计数
        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        // 记录开始时间
        long startTime = System.currentTimeMillis();

        // 定义线程数
        int threadSize = 100;

        // 定义门闩 jdk封装的计数器 底层通过AQS实现的线程安全
        CountDownLatch countDownLatch = new CountDownLatch(threadSize);

        // 开启100个线程
        for (int i = 0; i < threadSize; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    // 模拟用户的10次访问
                    for (int j = 0; j < 10; j++) {
                        try {
                            request();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            // 计数-1
                            countDownLatch.countDown();
                        }
                    }
                }
            });

            // 启动线程
            thread.start();
        }

        // 保证当100个线程执行完毕时，执行后面的代码
        countDownLatch.await();

        // 记录结束时间
        long endTime = System.currentTimeMillis();

        // 打印最后一个执行的线程+最终输出的结果数
        System.out.println(Thread.currentThread().getName()+" count: "+count + " totalTime: "+(endTime-startTime));
    }
}
